package com.investmenttechnologyservices.rest;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Logger;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.inject.Injector;

@SuppressWarnings("serial")
public class RestServlet extends HttpServlet {
	
    private static final Logger log = Logger.getLogger(RestServlet.class.getName());

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		
		PrintWriter out = response.getWriter();
		
		ArrayList<String> pathElements = getPathElements(request);
				
		try {
			Object ret = callService(pathElements, request.getParameterMap(), request, response);
			if (ret != null) {
				ObjectMapper mapper = new ObjectMapper();
				mapper.writeValue(out, ret);
			}
		} catch (Exception e) {
			response.setStatus(400);
			response.resetBuffer();
//			e.printStackTrace();
			out.println(e.toString());
		}
		
		out.close();
	}

	@Override
	public void doPost(HttpServletRequest req, HttpServletResponse res)
			throws ServletException, IOException {
		
		log.entering(RestServlet.class.getName(), "doPost");

		//res.setContentType("text/plain");

		PrintWriter out = res.getWriter();

		ArrayList<String> pathElements = getPathElements(req);
				
		try {
			Object ret = callService(pathElements, req.getParameterMap(), req, res);
			if (ret != null) {
				ObjectMapper mapper = new ObjectMapper();
				mapper.writeValue(out, ret);
			}
		} catch (Exception e) {
			res.setStatus(400);
			res.resetBuffer();
			out.println(e.toString());
		}
		
		out.close();

		
//			res.setContentType("text/plain");

	}

	private ArrayList<String> getPathElements(HttpServletRequest request) {
		ArrayList<String> pathElements = new ArrayList<String>();
		StringTokenizer st = new StringTokenizer(request.getPathInfo(), "/");
		while (st.hasMoreTokens()) {
			String pathElement = st.nextToken();
			pathElements.add(pathElement);
		}
		return pathElements;
	}

	/**
	 * @param pathElements
	 * 			element 0 = service name
	 *          element 1 = method name
	 *          element 2-> = parameters to pass
	 * @param parameterMap
	 * @param res 
	 * @param req 
	 */
	private Object callService(ArrayList<String> pathElements, Map<?, ?> parameterMap, 
			HttpServletRequest req, HttpServletResponse res) throws ServletException {
		System.err.println("RestServlet.callService: " + pathElements);
		
		ServletContext sc = this.getServletContext();
		Injector inj = (Injector) sc.getAttribute(Injector.class.getName());
		
		if (pathElements.size() < 2) {
			throw new ServletException("Number of path elements must be >= 2");
		}
		
		String serviceName = pathElements.get(0);
		String methodName = pathElements.get(1);

		int reqRes = 0;
		if (req != null && res != null) {
			reqRes = 2;
		}
		int reqularParameters = pathElements.size()-2;
		
//		inj.getInstance(arg0)

		Class<?> serviceClass = ServiceRegistry.getServiceClass(serviceName);
		Object serviceObject = inj.getInstance(serviceClass);
		
		Class<?>[] parameterTypes = new Class<?>[reqularParameters + reqRes];
		Arrays.fill(parameterTypes, String.class);
		if (reqRes == 2) {
			parameterTypes[parameterTypes.length-2] = HttpServletRequest.class;
			parameterTypes[parameterTypes.length-1] = HttpServletResponse.class;
		}
		
		Method method = null;
		try {
			method = serviceClass.getDeclaredMethod(methodName, parameterTypes);
		} catch (Exception e) {
			String msg = "Error finding method " + methodName + " in service " + serviceName + " - " + e.getLocalizedMessage();
			log.severe(msg);
			throw new ServletException(msg);
		}
		Object[] args = new Object[reqularParameters+reqRes];
		for (int i=0; i<reqularParameters; i++) {
			args[i] = pathElements.get(i+2);
		}
		if (reqRes == 2) {
			args[args.length-2] = req;
			args[args.length-1] = res;
		}
		Object ret = null;
		try {
			method.setAccessible(true);
			ret = method.invoke(serviceObject, args);
		} catch (Exception e) {
			String msg = "Error invoking method " + methodName + " in service " + serviceName + " - " + e.getLocalizedMessage();
			log.severe(msg);
			e.printStackTrace();
			throw new ServletException(msg);
		}
		return ret;
	}

}



//if (true) {
//StringWriter sw = new StringWriter();
//mapper.writeValue(sw, ret);
//String s = sw.toString();
//System.err.println("JSON returned from service");
//System.err.println("--start");
//System.err.println(s);
//System.err.println("--end");
//}

